Back to the base - plot
We’re going to use the DNase dataset, included in R’s distribution
You might want to read up a little on it, so type help(DNase) to do so - yes, nice datasets SHOULD be documented
head(DNase)
# Run conc density
# 1 1 0.04882812 0.017
# 2 1 0.04882812 0.018
# 3 1 0.19531250 0.121
# 4 1 0.19531250 0.124
# 5 1 0.39062500 0.206
# 6 1 0.39062500 0.215
plot(DNase$conc, DNase$density)

Back to the base - plot
Customizing this a little…
E.g. color the dots in blue, and add vertical lines for each value of concentration
plot(DNase$conc, DNase$density,
ylab = attr(DNase, "labels")$y,
xlab = paste(attr(DNase, "labels")$x, attr(DNase, "units")$x),
pch = 3,
col = "blue")
abline(v = unique(DNase$conc), lty = "dotted")

Back to the base - plot
Color the points by their value in the Run column
plot(DNase$con, DNase$density, col = DNase$Run)

Back to the base - plot
What if you need to check the distribution of a variable?
What if you wanted a boxplot?
Can you check out how to split the boxplots by their Run?
par(mfrow = c(1, 2))
hist(DNase$density)
boxplot(density ~ Run, DNase)

What’s “wrong” with this?
While it is effective to quickly produce out of the box figures…
- there is not global overview and parameterization of the visualization
- the layout decisions have to be made up upfront
- every aspect of the figure is customised locally as function arguments (one could argue that this is a plus)
- there is no unified type of data across all functions which makes it efficient for some types of data (if they match), but also very heterogeneous in terms of interface - base graphics functions will work with various inputs: a data.frame, vectors, a formula,…
- defaults (colours in particular!) are poorly chosen
We’ll soon use a visualization framework called the grammar of graphics (as in ggplot2) - enabling step by step construction of high quality graphics in a logical and elegant manner.
But first: hi-dim data time!
Hiiragi2013
A gene expression microarray dataset that reports the transcriptomes of around 100 individual cells from mouse embryos at different time points in early development
library("Hiiragi2013")
data("x")
dim(Biobase::exprs(x))
# [1] 45101 101
head(pData(x), n = 2)
# File.name Embryonic.day Total.number.of.cells lineage genotype
# 1 E3.25 1_C32_IN E3.25 32 WT
# 2 E3.25 2_C32_IN E3.25 32 WT
# ScanDate sampleGroup sampleColour
# 1 E3.25 2011-03-16 E3.25 #CAB2D6
# 2 E3.25 2011-03-16 E3.25 #CAB2D6
We’ll do some wrangling on that soon, so let’s create
dftx <- data.frame(t(Biobase::exprs(x)), pData(x))
ggplot2: the grammar of graphics
The components of ggplot2’s grammar of graphics are
- one or more datasets,
- one or more geometric objects that serve as the visual representations of the data, – for instance, points, lines, rectangles, contours,
- descriptions of how the variables in the data are mapped to visual properties (aesthetics) of the geometric objects, and an associated scale (e. g., linear, logarithmic, rank),
- one or more coordinate systems,
- statistical summarization rules,
- a facet specification, i.e. the use of multiple similar subplots to look at subsets of the same data,
- optional parameters that affect the layout and rendering, such text size, font and alignment, legend positions.
Simple form:
ggplot(data = <DATA>, mapping = aes(<MAPPINGS>)) + <GEOM_FUNCTION>()
Advantages of the ggplot2 framework
- The ease of getting a good looking plot
- Easy customization
- A lot of necessary data processing is done for you
- Clear syntax
- Easy multidimensional approach
- Decent default color scheme as a default
- Lots of extensions
Re-doing the DNase plot…
Exercise: create a similar plot of DNase density vs concentration, like we just did for base graphics.
library("ggplot2")
ggplot()

ggplot(data = DNase)

ggplot(data = DNase,
mapping = aes(x = conc, y = density))

ggplot(data = DNase,
mapping = aes(x = conc, y = density)) +
geom_point()

p <- ggplot(data = DNase,
mapping = aes(x = conc, y = density)) +
geom_point()
p

p + geom_point(aes(color = Run))

p + geom_point(aes(color = as.character(Run)))

# ggsave("DNAse-histogram-demo.pdf", plot = p) # check out the docs for this!
Which one is your favorite? Why?
Exploring Hiiragi2013
What are we doing here?
ggplot(dftx, aes(x = X1426642_at, y = X1418765_at)) +
geom_point(shape = 1) +
geom_smooth(method = "loess")
–
and here?
ggplot(dftx, aes(x = X1426642_at, y = X1418765_at)) +
geom_point(aes(color = sampleColour), shape = 19) +
geom_smooth(method = "loess") +
scale_color_discrete()
Visualizing 1D data
A common task in biological data analysis is the comparison between several samples of univariate measurements. In this section we’ll explore some possibilities for visualizing and comparing such samples. As an example, we’ll use the intensities of a set of four genes: Fgf4, Gata4, Gata6 and Sox2
selectedProbes = c(Fgf4 = "1420085_at", Gata4 = "1418863_at",
Gata6 = "1425463_at", Sox2 = "1416967_at")
library("dplyr")
library("tidyr")
tmp <- data.frame(t(exprs(x[selectedProbes, ])))
names(tmp) <- names(selectedProbes)
tmp$sample <- rownames(tmp)
head(tmp)
# Fgf4 Gata4 Gata6 Sox2 sample
# 1 E3.25 3.027715 4.843137 5.500618 1.731217 1 E3.25
# 2 E3.25 9.293016 5.530016 6.160900 9.697038 2 E3.25
# 3 E3.25 2.940142 4.418059 4.584961 4.161240 3 E3.25
# 4 E3.25 9.715243 5.982314 4.753439 9.540123 4 E3.25
# 5 E3.25 8.924228 4.923580 4.629728 8.705340 5 E3.25
# 6 E3.25 11.325952 4.068520 4.165692 8.696228 6 E3.25
genes <- gather(tmp, key = "gene", value = "expression", -sample)
head(genes)
# sample gene expression
# 1 1 E3.25 Fgf4 3.027715
# 2 2 E3.25 Fgf4 9.293016
# 3 3 E3.25 Fgf4 2.940142
# 4 4 E3.25 Fgf4 9.715243
# 5 5 E3.25 Fgf4 8.924228
# 6 6 E3.25 Fgf4 11.325952
This genes data.frame is in the so-called tidy format!
ggplot2 LOVES tidy data
1D: barplots, boxplots, dot, jitter, violins
ggplot(genes, aes(x = gene, y = expression)) +
stat_summary(fun = mean, geom = "bar")

Out of the following representations, let’s discuss which one you prefer most/least
p <- ggplot(genes, aes( x = gene, y = expression, fill = gene))
p + geom_boxplot()

Try now some more options on this object p. Try to add jittered points, or use a violin plot
p + geom_jitter(aes(colour = gene))

p + geom_violin()

p + geom_dotplot(binaxis = "y", binwidth = 1/6,
stackdir = "center", stackratio = 0.75,
aes(color = gene))

library("ggbeeswarm")
p + geom_beeswarm(aes(color = gene))

library("ggforce")
p + geom_sina(aes(color = gene))

You can even stack multiple geom_s on another!
1D: densities, histograms
genes %>%
filter(gene == "Gata4") %>%
ggplot(aes(x = expression)) + geom_histogram()

ggplot(genes, aes(x = expression, color = gene)) +
geom_density()

ggplot(genes, aes(x = expression, color = gene)) +
geom_density() +
theme_bw()

There are soooo many themes available - I like clean ones, but often it depends on your purpose!
Visualizing 2D data
dfx <- as.data.frame(Biobase::exprs(x))
scp <- ggplot(dfx, aes(x= `59 E4.5 (PE)`,
y = `92 E4.5 (FGF4-KO)`))
scp + geom_point()

Can you think of a way to reduce the overplotting here?
scp + geom_point(alpha = 0.3)

scp + geom_density2d(h = 0.5, bins = 60)

scp + geom_hex() + coord_fixed()

Visualizing data along more dimensions
When visualising data along additional dimension, we can parameterize the points by setting their shape, colour, size and transparency, that can be set with point aesthetics such as fill, color (or colour), shape, size and alpha.
A very powerful way to represent data along additional dimensions is facetting, i.e. producing sub-plots for different subsets of the data. Below, we first re-annotate the data using some regular expressions
ggplot(dftx, aes(x = X1426642_at, y = X1418765_at, colour = lineage)) +
geom_point()

ggplot(dftx, aes(x = X1426642_at, y = X1418765_at)) +
geom_point() +
facet_grid( . ~ lineage )

ggplot(dftx,
aes(x = X1426642_at, y = X1418765_at)) +
geom_point() +
facet_grid( Embryonic.day ~ lineage )

Your turn: Use facets to visualise the distribution of the four Fgf4, Gata4, Gata6 and Sox2 genes in the genes data using histograms.
ggplot(genes, aes(x = expression)) +
geom_histogram() +
facet_wrap(~ gene)

Interactive visualizations
p2 <- p + geom_jitter(aes(colour = gene))
library("plotly")
ggplotly(p2)
Sometimes this is all you might need!
An appetizer for RNA-seq?
…or many other high-dimensional data
- MA plot
- volcano plot
- heatmaps
- PCA plot
- tSNE plot
- other genomic data (karyoplots, …)
What do these plots do? Let’s discuss together.
An appetizer for RNA-seq?
library("pheatmap")
library("dplyr")
groups <- group_by(pData(x), sampleGroup) %>%
summarise(n = n(), color = unique(sampleColour))
groupColor <- setNames(groups$color, groups$sampleGroup)
topGenes <- order(rowVars(Biobase::exprs(x)), decreasing = TRUE)[1:500]
rowCenter <- function(x) { x - rowMeans(x) }
pheatmap( rowCenter(Biobase::exprs(x)[ topGenes, ] ),
show_rownames = FALSE, show_colnames = FALSE,
breaks = seq(-5, +5, length = 101),
annotation_col =
pData(x)[, c("sampleGroup", "genotype", "Embryonic.day", "ScanDate") ],
annotation_colors = list(
sampleGroup = groupColor,
genotype = c(`FGF4-KO` = "chocolate1", `WT` = "azure2"),
Embryonic.day = setNames(brewer.pal(9, "Blues")[c(3, 6, 9)],
c("E3.25", "E3.5", "E4.5")),
ScanDate = setNames(brewer.pal(nlevels(x$ScanDate), "YlGn"),
levels(x$ScanDate))
),
cutree_rows = 4
)

Got a nice viz?
Let’s try to dissect that
A checklist
- Appropriate plot type for results - Might be a boxplot, a scatterplot, a linear regression fit … many options
- Plot is well organised - The independent (explanatory) variable is on the x and the dependent (respnse) variable is on the y axis
- X and Y axes use correct units - Having proper symbols (for alpha, beta, etc.) and super/subscript where needed
- X and Y axes easy to read - Beware awkward fonts and tiny letters
- Clear informative legend - It’s easy to tell apart what points/lines on the graph represent
- Plot is not cluttered - Don’t put all results on one plot, give them space to shine
- Clear and consistent colour scheme - Stick with the same colours for the same variables, avoid red/green combinations which might look the same to colourblind people
- Plot is the right dimensions - Avoid overlapping labels and points/lines which merge together and make your graph longer/wider if needed
- Measures of uncertainty where appropriate - Error bars, confidence and credible intervals, remember to say in the caption what they are
- Concise and informative caption - Remember to include what the data points show (raw data? Model predictions?), what is the sample size for each treatment, the effect size and what measure of uncertainty accompanies it
Summary
Visualizing data is one of the most important activities in applied statistics & in science.
There is a large number of good (and bad) practices -> you can quickly see whether a certain graphic is effective in conveying its message
Important options:
- plot type (what is called a geom in ggplot2)
- proportions (incl. aspect ratios)
- colors.
The grammar of graphics is a powerful set of concepts to reason about graphics and to communicate our intentions for a data visualization to a computer.
Creating your own visualizations is in many ways like good writing. It is extremely important, but there is no simple recipe for it.
Look carefully at lots of visualizations made by others & experiment with making your own visualizations to learn the ropes
Yes, we just scratched the surface! Data viz is a scientific discipline in its own
LS0tCnRpdGxlOiA+CiAgRGF0YSB2aXogLSBoYW5kcy1vbiBzZXNzaW9uCnN1YnRpdGxlOiA+CiAgR1RJUEkgU3VtbWVyU2Nob29sCiAgPHAgYWxpZ249ImNlbnRlciI+CiAgPGEgaHJlZj0iaHR0cHM6Ly9pbWJlaW1haW56LmdpdGh1Yi5pby9HVElQSTIwMjIiPjxpbWcgc3JjPSJpbWFnZXMvZ3RpcGlfbG9nby5wbmciIGFsdD0iIiBoZWlnaHQ9IjE1MCIvPjwvYT4KICA8L3A+CmF1dGhvcjoKLSBuYW1lOiA8YSBocmVmPSJodHRwczovL2ZlZGVyaWNvbWFyaW5pLmdpdGh1Yi5pbyI+RmVkZXJpY28gTWFyaW5pIChtYXJpbmlmQHVuaS1tYWluei5kZSk8L2E+PGJyPjxhIGhyZWY9Imh0dHBzOi8vd3d3LnVuaW1lZGl6aW4tbWFpbnouZGUvaW1iZWkvIj5JTUJFSSwgVW5pdmVyc2l0eSBNZWRpY2FsIENlbnRlciBNYWluejwvYT48YnI+PGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9GZWRlQmlvaW5mbyI+YHIgaWNvbnM6OmZvbnRhd2Vzb21lKCd0d2l0dGVyJylgIGBARmVkZUJpb2luZm9gPC9hPgpkYXRlOiAiMjAyMi8wNi8wMiIKb3V0cHV0OiAKICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBjb2xsYXBzZSA9IFRSVUUsCiAgY29tbWVudCA9ICIjIiwKICBlcnJvciA9IEZBTFNFLAogIHdhcm5pbmcgPSBGQUxTRSwKICBtZXNzYWdlID0gRkFMU0UKKQpgYGAKCgoKIyBCYWNrIHRvIHRoZSBiYXNlIC0gYHBsb3RgCgpXZSdyZSBnb2luZyB0byB1c2UgdGhlIGBETmFzZWAgZGF0YXNldCwgaW5jbHVkZWQgaW4gUidzIGRpc3RyaWJ1dGlvbgoKWW91IG1pZ2h0IHdhbnQgdG8gcmVhZCB1cCBhIGxpdHRsZSBvbiBpdCwgc28gdHlwZSBgaGVscChETmFzZSlgIHRvIGRvIHNvIC0geWVzLCBuaWNlIGRhdGFzZXRzIFNIT1VMRCBiZSBkb2N1bWVudGVkCgpgYGB7cn0KaGVhZChETmFzZSkKcGxvdChETmFzZSRjb25jLCBETmFzZSRkZW5zaXR5KQpgYGAKCi0tLQoKIyBCYWNrIHRvIHRoZSBiYXNlIC0gYHBsb3RgCgpDdXN0b21pemluZyB0aGlzIGEgbGl0dGxlLi4uCgpFLmcuIGNvbG9yIHRoZSBkb3RzIGluIGJsdWUsIGFuZCBhZGQgdmVydGljYWwgbGluZXMgZm9yIGVhY2ggdmFsdWUgb2YgY29uY2VudHJhdGlvbgoKPGRldGFpbHM+CgpgYGB7cn0KcGxvdChETmFzZSRjb25jLCBETmFzZSRkZW5zaXR5LAogIHlsYWIgPSBhdHRyKEROYXNlLCAibGFiZWxzIikkeSwKICB4bGFiID0gcGFzdGUoYXR0cihETmFzZSwgImxhYmVscyIpJHgsIGF0dHIoRE5hc2UsICJ1bml0cyIpJHgpLAogIHBjaCA9IDMsCiAgY29sID0gImJsdWUiKQphYmxpbmUodiA9IHVuaXF1ZShETmFzZSRjb25jKSwgbHR5ID0gImRvdHRlZCIpCmBgYAoKPC9kZXRhaWxzPgoKLS0tCgojIEJhY2sgdG8gdGhlIGJhc2UgLSBgcGxvdGAKCkNvbG9yIHRoZSBwb2ludHMgYnkgdGhlaXIgdmFsdWUgaW4gdGhlIFJ1biBjb2x1bW4KCjxkZXRhaWxzPgoKYGBge3J9CnBsb3QoRE5hc2UkY29uLCBETmFzZSRkZW5zaXR5LCBjb2wgPSBETmFzZSRSdW4pCmBgYAoKPC9kZXRhaWxzPgoKCgotLS0KCiMgQmFjayB0byB0aGUgYmFzZSAtIGBwbG90YAoKV2hhdCBpZiB5b3UgbmVlZCB0byBjaGVjayB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgdmFyaWFibGU/CgpXaGF0IGlmIHlvdSB3YW50ZWQgYSBib3hwbG90PwoKQ2FuIHlvdSBjaGVjayBvdXQgaG93IHRvIHNwbGl0IHRoZSBib3hwbG90cyBieSB0aGVpciBSdW4/Cgo8ZGV0YWlscz4KCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NX0KcGFyKG1mcm93ID0gYygxLCAyKSkKaGlzdChETmFzZSRkZW5zaXR5KSAKYm94cGxvdChkZW5zaXR5IH4gUnVuLCBETmFzZSkKYGBgCgo8L2RldGFpbHM+CgotLS0KCiMgV2hhdCdzICJ3cm9uZyIgd2l0aCB0aGlzPwoKV2hpbGUgaXQgaXMgZWZmZWN0aXZlIHRvIHF1aWNrbHkgcHJvZHVjZSBvdXQgb2YgdGhlIGJveCBmaWd1cmVzLi4uCgotIHRoZXJlIGlzIG5vdCBnbG9iYWwgb3ZlcnZpZXcgYW5kIHBhcmFtZXRlcml6YXRpb24gb2YgdGhlIHZpc3VhbGl6YXRpb24KLSB0aGUgbGF5b3V0IGRlY2lzaW9ucyBoYXZlIHRvIGJlIG1hZGUgdXAgdXBmcm9udAotIGV2ZXJ5IGFzcGVjdCBvZiB0aGUgZmlndXJlIGlzIGN1c3RvbWlzZWQgbG9jYWxseSBhcyBmdW5jdGlvbiBhcmd1bWVudHMgKG9uZSBjb3VsZCBhcmd1ZSB0aGF0IHRoaXMgaXMgYSBwbHVzKQotIHRoZXJlIGlzIG5vIHVuaWZpZWQgdHlwZSBvZiBkYXRhIGFjcm9zcyBhbGwgZnVuY3Rpb25zIHdoaWNoIG1ha2VzIGl0IGVmZmljaWVudCBmb3Igc29tZSB0eXBlcyBvZiBkYXRhIChpZiB0aGV5IG1hdGNoKSwgYnV0IGFsc28gdmVyeSBoZXRlcm9nZW5lb3VzIGluIHRlcm1zIG9mIGludGVyZmFjZSAtIGJhc2UgZ3JhcGhpY3MgZnVuY3Rpb25zIHdpbGwgd29yayB3aXRoIHZhcmlvdXMgaW5wdXRzOiBhIGRhdGEuZnJhbWUsIHZlY3RvcnMsIGEgZm9ybXVsYSwuLi4KLSBkZWZhdWx0cyAoY29sb3VycyBpbiBwYXJ0aWN1bGFyISkgYXJlIHBvb3JseSBjaG9zZW4KCldlJ2xsIHNvb24gdXNlIGEgdmlzdWFsaXphdGlvbiBmcmFtZXdvcmsgY2FsbGVkIHRoZSAqZ3JhbW1hciBvZiBncmFwaGljcyogKGFzIGluIGBnZ3Bsb3QyYCkgLSBlbmFibGluZyBzdGVwIGJ5IHN0ZXAgY29uc3RydWN0aW9uIG9mIGhpZ2ggcXVhbGl0eSBncmFwaGljcyBpbiBhIGxvZ2ljYWwgYW5kIGVsZWdhbnQgbWFubmVyLiAKCkJ1dCBmaXJzdDogaGktZGltIGRhdGEgdGltZSEKCi0tLQoKIyBgSGlpcmFnaTIwMTNgCgpBIGdlbmUgZXhwcmVzc2lvbiBtaWNyb2FycmF5IGRhdGFzZXQgdGhhdCByZXBvcnRzIHRoZSB0cmFuc2NyaXB0b21lcyBvZiBhcm91bmQgMTAwIGluZGl2aWR1YWwgY2VsbHMgZnJvbSBtb3VzZSBlbWJyeW9zIGF0IGRpZmZlcmVudCB0aW1lIHBvaW50cyBpbiBlYXJseSBkZXZlbG9wbWVudAoKYGBge3J9CmxpYnJhcnkoIkhpaXJhZ2kyMDEzIikKZGF0YSgieCIpCmRpbShCaW9iYXNlOjpleHBycyh4KSkKCmhlYWQocERhdGEoeCksIG4gPSAyKQpgYGAKCldlJ2xsIGRvIHNvbWUgd3JhbmdsaW5nIG9uIHRoYXQgc29vbiwgc28gbGV0J3MgY3JlYXRlIAoKYGBge3J9CmRmdHggPC0gZGF0YS5mcmFtZSh0KEJpb2Jhc2U6OmV4cHJzKHgpKSwgcERhdGEoeCkpCmBgYAoKLS0tCgojIGBnZ3Bsb3QyYDogdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MKClRoZSBjb21wb25lbnRzIG9mIGBnZ3Bsb3QyYCdzIGdyYW1tYXIgb2YgZ3JhcGhpY3MgYXJlCgoxLiBvbmUgb3IgbW9yZSBkYXRhc2V0cywKMS4gb25lIG9yIG1vcmUgZ2VvbWV0cmljIG9iamVjdHMgdGhhdCBzZXJ2ZSBhcyB0aGUgdmlzdWFsIHJlcHJlc2VudGF0aW9ucyBvZiB0aGUgZGF0YSwg4oCTIGZvciBpbnN0YW5jZSwgcG9pbnRzLCBsaW5lcywgcmVjdGFuZ2xlcywgY29udG91cnMsCjEuIGRlc2NyaXB0aW9ucyBvZiBob3cgdGhlIHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBhcmUgbWFwcGVkIHRvIHZpc3VhbCBwcm9wZXJ0aWVzIChhZXN0aGV0aWNzKSBvZiB0aGUgZ2VvbWV0cmljIG9iamVjdHMsIGFuZCBhbiBhc3NvY2lhdGVkIHNjYWxlIChlLiBnLiwgbGluZWFyLCBsb2dhcml0aG1pYywgcmFuayksCjEuIG9uZSBvciBtb3JlIGNvb3JkaW5hdGUgc3lzdGVtcywKMS4gc3RhdGlzdGljYWwgc3VtbWFyaXphdGlvbiBydWxlcywKMS4gYSBmYWNldCBzcGVjaWZpY2F0aW9uLCBpLmUuIHRoZSB1c2Ugb2YgbXVsdGlwbGUgc2ltaWxhciBzdWJwbG90cyB0byBsb29rIGF0IHN1YnNldHMgb2YgdGhlIHNhbWUgZGF0YSwKMS4gb3B0aW9uYWwgcGFyYW1ldGVycyB0aGF0IGFmZmVjdCB0aGUgbGF5b3V0IGFuZCByZW5kZXJpbmcsIHN1Y2ggdGV4dCBzaXplLCBmb250IGFuZCBhbGlnbm1lbnQsIGxlZ2VuZCBwb3NpdGlvbnMuCgpTaW1wbGUgZm9ybToKCmBgYApnZ3Bsb3QoZGF0YSA9IDxEQVRBPiwgbWFwcGluZyA9IGFlcyg8TUFQUElOR1M+KSkgKyAgPEdFT01fRlVOQ1RJT04+KCkKYGBgCgotLS0KCiMgQWR2YW50YWdlcyBvZiB0aGUgYGdncGxvdDJgIGZyYW1ld29yawoKLSBUaGUgZWFzZSBvZiBnZXR0aW5nIGEgZ29vZCBsb29raW5nIHBsb3QKLSBFYXN5IGN1c3RvbWl6YXRpb24KLSBBIGxvdCBvZiBuZWNlc3NhcnkgZGF0YSBwcm9jZXNzaW5nIGlzIGRvbmUgZm9yIHlvdQotIENsZWFyIHN5bnRheAotIEVhc3kgbXVsdGlkaW1lbnNpb25hbCBhcHByb2FjaAotIERlY2VudCBkZWZhdWx0IGNvbG9yIHNjaGVtZSBhcyBhIGRlZmF1bHQKLSBMb3RzIG9mIGV4dGVuc2lvbnMKCi0tLQoKIyBSZS1kb2luZyB0aGUgRE5hc2UgcGxvdC4uLgoKRXhlcmNpc2U6IGNyZWF0ZSBhIHNpbWlsYXIgcGxvdCBvZiBETmFzZSBkZW5zaXR5IHZzIGNvbmNlbnRyYXRpb24sIGxpa2Ugd2UganVzdCBkaWQgZm9yIGJhc2UgZ3JhcGhpY3MuCgo8ZGV0YWlscz4KCmBgYHtyfQpsaWJyYXJ5KCJnZ3Bsb3QyIikKZ2dwbG90KCkKZ2dwbG90KGRhdGEgPSBETmFzZSkKZ2dwbG90KGRhdGEgPSBETmFzZSwKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGNvbmMsIHkgPSBkZW5zaXR5KSkKCmdncGxvdChkYXRhID0gRE5hc2UsCiAgICAgICBtYXBwaW5nID0gYWVzKHggPSBjb25jLCB5ID0gZGVuc2l0eSkpICsgCiAgZ2VvbV9wb2ludCgpCnAgPC0gZ2dwbG90KGRhdGEgPSBETmFzZSwKICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gY29uYywgeSA9IGRlbnNpdHkpKSArIAogIGdlb21fcG9pbnQoKQpwCnAgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFJ1bikpCnAgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGFzLmNoYXJhY3RlcihSdW4pKSkKIyBnZ3NhdmUoIkROQXNlLWhpc3RvZ3JhbS1kZW1vLnBkZiIsIHBsb3QgPSBwKSAjIGNoZWNrIG91dCB0aGUgZG9jcyBmb3IgdGhpcyEKYGBgCgo8L2RldGFpbHM+CgpXaGljaCBvbmUgaXMgeW91ciBmYXZvcml0ZT8gV2h5PwoKPCEtLSBkZWZpbmUgYSBtYXBwaW5nICh1c2luZyB0aGUgYWVzdGhldGljIChhZXMpIGZ1bmN0aW9uKSwgYnkgc2VsZWN0aW5nIHRoZSB2YXJpYWJsZXMgdG8gYmUgcGxvdHRlZCBhbmQgc3BlY2lmeWluZyBob3cgdG8gcHJlc2VudCB0aGVtIGluIHRoZSBncmFwaCwgZS5nLiBhcyB4L3kgcG9zaXRpb25zIG9yIGNoYXJhY3RlcmlzdGljcyBzdWNoIGFzIHNpemUsIHNoYXBlLCBjb2xvdXIsIGV0Yy4gLS0+CgotLS0KCiMgRXhwbG9yaW5nIGBIaWlyYWdpMjAxM2AKCldoYXQgYXJlIHdlIGRvaW5nIGhlcmU/CgpgYGB7ciBldmFsPUZBTFNFfQpnZ3Bsb3QoZGZ0eCwgYWVzKHggPSBYMTQyNjY0Ml9hdCwgeSA9IFgxNDE4NzY1X2F0KSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIikKYGBgCgotLQoKYW5kIGhlcmU/CgpgYGB7ciBldmFsPUZBTFNFfQpnZ3Bsb3QoZGZ0eCwgYWVzKHggPSBYMTQyNjY0Ml9hdCwgeSA9IFgxNDE4NzY1X2F0KSkgICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHNhbXBsZUNvbG91ciksIHNoYXBlID0gMTkpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiKSArCiAgc2NhbGVfY29sb3JfZGlzY3JldGUoKQpgYGAKCi0tLQoKIyBWaXN1YWxpemluZyAxRCBkYXRhCgpBIGNvbW1vbiB0YXNrIGluIGJpb2xvZ2ljYWwgZGF0YSBhbmFseXNpcyBpcyB0aGUgY29tcGFyaXNvbiBiZXR3ZWVuIHNldmVyYWwgc2FtcGxlcyBvZiB1bml2YXJpYXRlIG1lYXN1cmVtZW50cy4gSW4gdGhpcyBzZWN0aW9uIHdl4oCZbGwgZXhwbG9yZSBzb21lIHBvc3NpYmlsaXRpZXMgZm9yIHZpc3VhbGl6aW5nIGFuZCBjb21wYXJpbmcgc3VjaCBzYW1wbGVzLiBBcyBhbiBleGFtcGxlLCB3ZeKAmWxsIHVzZSB0aGUgaW50ZW5zaXRpZXMgb2YgYSBzZXQgb2YgZm91ciBnZW5lczogRmdmNCwgR2F0YTQsIEdhdGE2IGFuZCBTb3gyCgpgYGB7cn0Kc2VsZWN0ZWRQcm9iZXMgPSBjKEZnZjQgPSAiMTQyMDA4NV9hdCIsIEdhdGE0ID0gIjE0MTg4NjNfYXQiLAogICAgICAgICAgICAgICAgICAgR2F0YTYgPSAiMTQyNTQ2M19hdCIsIFNveDIgPSAiMTQxNjk2N19hdCIpCmxpYnJhcnkoImRwbHlyIikKbGlicmFyeSgidGlkeXIiKQp0bXAgPC0gZGF0YS5mcmFtZSh0KGV4cHJzKHhbc2VsZWN0ZWRQcm9iZXMsIF0pKSkKbmFtZXModG1wKSA8LSBuYW1lcyhzZWxlY3RlZFByb2JlcykKdG1wJHNhbXBsZSA8LSByb3duYW1lcyh0bXApCmhlYWQodG1wKQpnZW5lcyA8LSBnYXRoZXIodG1wLCBrZXkgPSAiZ2VuZSIsIHZhbHVlID0gImV4cHJlc3Npb24iLCAtc2FtcGxlKQpoZWFkKGdlbmVzKQpgYGAKClRoaXMgYGdlbmVzYCBkYXRhLmZyYW1lIGlzIGluIHRoZSBzby1jYWxsZWQgdGlkeSBmb3JtYXQhICAKYGdncGxvdDJgIExPVkVTIHRpZHkgZGF0YQoKLS0tCgojIDFEOiBiYXJwbG90cywgYm94cGxvdHMsIGRvdCwgaml0dGVyLCB2aW9saW5zCgpgYGB7cn0KZ2dwbG90KGdlbmVzLCBhZXMoeCA9IGdlbmUsIHkgPSBleHByZXNzaW9uKSkgKwogIHN0YXRfc3VtbWFyeShmdW4gPSBtZWFuLCBnZW9tID0gImJhciIpCmBgYAoKT3V0IG9mIHRoZSBmb2xsb3dpbmcgcmVwcmVzZW50YXRpb25zLCBsZXQncyBkaXNjdXNzIHdoaWNoIG9uZSB5b3UgcHJlZmVyIG1vc3QvbGVhc3QKCmBgYHtyfQpwIDwtIGdncGxvdChnZW5lcywgYWVzKCB4ID0gZ2VuZSwgeSA9IGV4cHJlc3Npb24sIGZpbGwgPSBnZW5lKSkKcCArIGdlb21fYm94cGxvdCgpCmBgYAoKVHJ5IG5vdyBzb21lIG1vcmUgb3B0aW9ucyBvbiB0aGlzIG9iamVjdCBgcGAuIFRyeSB0byBhZGQgaml0dGVyZWQgcG9pbnRzLCBvciB1c2UgYSB2aW9saW4gcGxvdAoKPGRldGFpbHM+CgpgYGB7cn0KcCArIGdlb21faml0dGVyKGFlcyhjb2xvdXIgPSBnZW5lKSkKcCArIGdlb21fdmlvbGluKCkKcCArIGdlb21fZG90cGxvdChiaW5heGlzID0gInkiLCBiaW53aWR0aCA9IDEvNiwKICAgICAgIHN0YWNrZGlyID0gImNlbnRlciIsIHN0YWNrcmF0aW8gPSAwLjc1LAogICAgICAgYWVzKGNvbG9yID0gZ2VuZSkpCmxpYnJhcnkoImdnYmVlc3dhcm0iKQpwICsgZ2VvbV9iZWVzd2FybShhZXMoY29sb3IgPSBnZW5lKSkKbGlicmFyeSgiZ2dmb3JjZSIpCnAgKyBnZW9tX3NpbmEoYWVzKGNvbG9yID0gZ2VuZSkpCmBgYAoKPC9kZXRhaWxzPgoKWW91IGNhbiBldmVuIHN0YWNrIG11bHRpcGxlIGBnZW9tX2BzIG9uIGFub3RoZXIhCgojIDFEOiBkZW5zaXRpZXMsIGhpc3RvZ3JhbXMKCmBgYHtyfQpnZW5lcyAlPiUKICBmaWx0ZXIoZ2VuZSA9PSAiR2F0YTQiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBleHByZXNzaW9uKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKYGBge3J9CmdncGxvdChnZW5lcywgYWVzKHggPSBleHByZXNzaW9uLCBjb2xvciA9IGdlbmUpKSArIAogIGdlb21fZGVuc2l0eSgpCgpnZ3Bsb3QoZ2VuZXMsIGFlcyh4ID0gZXhwcmVzc2lvbiwgY29sb3IgPSBnZW5lKSkgKyAKICBnZW9tX2RlbnNpdHkoKSArIAogIHRoZW1lX2J3KCkKCmBgYAoKVGhlcmUgYXJlIHNvb29vIG1hbnkgdGhlbWVzIGF2YWlsYWJsZSAtIEkgbGlrZSBjbGVhbiBvbmVzLCBidXQgb2Z0ZW4gaXQgZGVwZW5kcyBvbiB5b3VyIHB1cnBvc2UhCgotLS0KCiMgVmlzdWFsaXppbmcgMkQgZGF0YQoKYGBge3J9CmRmeCA8LSBhcy5kYXRhLmZyYW1lKEJpb2Jhc2U6OmV4cHJzKHgpKQpzY3AgPC0gZ2dwbG90KGRmeCwgYWVzKHg9IGA1OSBFNC41IChQRSlgLCAKICAgICAgICAgICAgICAgICAgICAgICB5ID0gYDkyIEU0LjUgKEZHRjQtS08pYCkpCnNjcCArIGdlb21fcG9pbnQoKQpgYGAKCgpDYW4geW91IHRoaW5rIG9mIGEgd2F5IHRvIHJlZHVjZSB0aGUgb3ZlcnBsb3R0aW5nIGhlcmU/Cgo8ZGV0YWlscz4KCmBgYHtyfQpzY3AgKyBnZW9tX3BvaW50KGFscGhhID0gMC4zKQpzY3AgKyBnZW9tX2RlbnNpdHkyZChoID0gMC41LCBiaW5zID0gNjApCnNjcCArIGdlb21faGV4KCkgKyBjb29yZF9maXhlZCgpCmBgYAoKPC9kZXRhaWxzPgoKLS0tCgojIFZpc3VhbGl6aW5nIGRhdGEgYWxvbmcgbW9yZSBkaW1lbnNpb25zCgpXaGVuIHZpc3VhbGlzaW5nIGRhdGEgYWxvbmcgYWRkaXRpb25hbCBkaW1lbnNpb24sIHdlIGNhbiBwYXJhbWV0ZXJpemUgdGhlIHBvaW50cyBieSBzZXR0aW5nIHRoZWlyIHNoYXBlLCBjb2xvdXIsIHNpemUgYW5kIHRyYW5zcGFyZW5jeSwgdGhhdCBjYW4gYmUgc2V0IHdpdGggcG9pbnQgYWVzdGhldGljcyBzdWNoIGFzIGZpbGwsIGNvbG9yIChvciBjb2xvdXIpLCBzaGFwZSwgc2l6ZSBhbmQgYWxwaGEuCgpBIHZlcnkgcG93ZXJmdWwgd2F5IHRvIHJlcHJlc2VudCBkYXRhIGFsb25nIGFkZGl0aW9uYWwgZGltZW5zaW9ucyBpcyBmYWNldHRpbmcsIGkuZS4gcHJvZHVjaW5nIHN1Yi1wbG90cyBmb3IgZGlmZmVyZW50IHN1YnNldHMgb2YgdGhlIGRhdGEuIEJlbG93LCB3ZSBmaXJzdCByZS1hbm5vdGF0ZSB0aGUgZGF0YSB1c2luZyBzb21lIHJlZ3VsYXIgZXhwcmVzc2lvbnMKCmBgYHtyfQpnZ3Bsb3QoZGZ0eCwgYWVzKHggPSBYMTQyNjY0Ml9hdCwgeSA9IFgxNDE4NzY1X2F0LCBjb2xvdXIgPSBsaW5lYWdlKSkgKwogIGdlb21fcG9pbnQoKQpnZ3Bsb3QoZGZ0eCwgYWVzKHggPSBYMTQyNjY0Ml9hdCwgeSA9IFgxNDE4NzY1X2F0KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfZ3JpZCggLiB+IGxpbmVhZ2UgKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGZ0eCwKICAgICAgIGFlcyh4ID0gWDE0MjY2NDJfYXQsIHkgPSBYMTQxODc2NV9hdCkpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X2dyaWQoIEVtYnJ5b25pYy5kYXkgfiBsaW5lYWdlICkKYGBgCgpZb3VyIHR1cm46IFVzZSBmYWNldHMgdG8gdmlzdWFsaXNlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGZvdXIgRmdmNCwgR2F0YTQsIEdhdGE2IGFuZCBTb3gyIGdlbmVzIGluIHRoZSBgZ2VuZXNgIGRhdGEgdXNpbmcgaGlzdG9ncmFtcy4KCjxkZXRhaWxzPgoKYGBge3J9CmdncGxvdChnZW5lcywgYWVzKHggPSBleHByZXNzaW9uKSkgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIGZhY2V0X3dyYXAofiBnZW5lKQpgYGAKCjwvZGV0YWlscz4KCi0tLQoKIyBJbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9ucwoKYGBge3J9CnAyIDwtIHAgKyBnZW9tX2ppdHRlcihhZXMoY29sb3VyID0gZ2VuZSkpCmxpYnJhcnkoInBsb3RseSIpCmdncGxvdGx5KHAyKQpgYGAKClNvbWV0aW1lcyB0aGlzIGlzIGFsbCB5b3UgbWlnaHQgbmVlZCEKCi0tLQoKIyBBbiBhcHBldGl6ZXIgZm9yIFJOQS1zZXE/CgouLi5vciBtYW55IG90aGVyIGhpZ2gtZGltZW5zaW9uYWwgZGF0YQoKKiBNQSBwbG90Ciogdm9sY2FubyBwbG90CiogaGVhdG1hcHMKKiBQQ0EgcGxvdAoqIHRTTkUgcGxvdAoqIG90aGVyIGdlbm9taWMgZGF0YSAoa2FyeW9wbG90cywgLi4uKQoKV2hhdCBkbyB0aGVzZSBwbG90cyBkbz8gTGV0J3MgZGlzY3VzcyB0b2dldGhlci4KCi0tLQoKIyBBbiBhcHBldGl6ZXIgZm9yIFJOQS1zZXE/CgpgYGB7cn0KbGlicmFyeSgicGhlYXRtYXAiKQpsaWJyYXJ5KCJkcGx5ciIpCmdyb3VwcyA8LSBncm91cF9ieShwRGF0YSh4KSwgc2FtcGxlR3JvdXApICU+JQogIHN1bW1hcmlzZShuID0gbigpLCBjb2xvciA9IHVuaXF1ZShzYW1wbGVDb2xvdXIpKQpncm91cENvbG9yIDwtIHNldE5hbWVzKGdyb3VwcyRjb2xvciwgZ3JvdXBzJHNhbXBsZUdyb3VwKQp0b3BHZW5lcyA8LSBvcmRlcihyb3dWYXJzKEJpb2Jhc2U6OmV4cHJzKHgpKSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6NTAwXQpyb3dDZW50ZXIgPC0gZnVuY3Rpb24oeCkgeyB4IC0gcm93TWVhbnMoeCkgfQpwaGVhdG1hcCggcm93Q2VudGVyKEJpb2Jhc2U6OmV4cHJzKHgpWyB0b3BHZW5lcywgXSApLAogIHNob3dfcm93bmFtZXMgPSBGQUxTRSwgc2hvd19jb2xuYW1lcyA9IEZBTFNFLAogIGJyZWFrcyA9IHNlcSgtNSwgKzUsIGxlbmd0aCA9IDEwMSksCiAgYW5ub3RhdGlvbl9jb2wgPQogICAgcERhdGEoeClbLCBjKCJzYW1wbGVHcm91cCIsICJnZW5vdHlwZSIsICJFbWJyeW9uaWMuZGF5IiwgIlNjYW5EYXRlIikgXSwKICBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoCiAgICBzYW1wbGVHcm91cCA9IGdyb3VwQ29sb3IsCiAgICBnZW5vdHlwZSA9IGMoYEZHRjQtS09gID0gImNob2NvbGF0ZTEiLCBgV1RgID0gImF6dXJlMiIpLAogICAgRW1icnlvbmljLmRheSA9IHNldE5hbWVzKGJyZXdlci5wYWwoOSwgIkJsdWVzIilbYygzLCA2LCA5KV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiRTMuMjUiLCAiRTMuNSIsICJFNC41IikpLAogICAgU2NhbkRhdGUgPSBzZXROYW1lcyhicmV3ZXIucGFsKG5sZXZlbHMoeCRTY2FuRGF0ZSksICJZbEduIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyh4JFNjYW5EYXRlKSkKICApLAogIGN1dHJlZV9yb3dzID0gNAopCmBgYAoKCi0tLQoKIyBHb3QgYSBuaWNlIHZpej8KCkxldCdzIHRyeSB0byBkaXNzZWN0IHRoYXQKCgotLS0KCiMgQSBjaGVja2xpc3QKCjEuIEFwcHJvcHJpYXRlIHBsb3QgdHlwZSBmb3IgcmVzdWx0cyAtIE1pZ2h0IGJlIGEgYm94cGxvdCwgYSBzY2F0dGVycGxvdCwgYSBsaW5lYXIgcmVncmVzc2lvbiBmaXQgLi4uIG1hbnkgb3B0aW9ucwoyLiBQbG90IGlzIHdlbGwgb3JnYW5pc2VkIC0JVGhlIGluZGVwZW5kZW50IChleHBsYW5hdG9yeSkgdmFyaWFibGUgaXMgb24gdGhlIHggYW5kIHRoZSBkZXBlbmRlbnQgKHJlc3Buc2UpIHZhcmlhYmxlIGlzIG9uIHRoZSB5IGF4aXMKMy4gWCBhbmQgWSBheGVzIHVzZSBjb3JyZWN0IHVuaXRzIC0gSGF2aW5nIHByb3BlciBzeW1ib2xzIChmb3IgYWxwaGEsIGJldGEsIGV0Yy4pIGFuZCBzdXBlci9zdWJzY3JpcHQgd2hlcmUgbmVlZGVkCjQuIFggYW5kIFkgYXhlcyBlYXN5IHRvIHJlYWQgLSBCZXdhcmUgYXdrd2FyZCBmb250cyBhbmQgdGlueSBsZXR0ZXJzCjUuIENsZWFyIGluZm9ybWF0aXZlIGxlZ2VuZCAtIEl0J3MgZWFzeSB0byB0ZWxsIGFwYXJ0IHdoYXQgcG9pbnRzL2xpbmVzIG9uIHRoZSBncmFwaCByZXByZXNlbnQKNi4gUGxvdCBpcyBub3QgY2x1dHRlcmVkIC0gRG9uJ3QgcHV0IGFsbCByZXN1bHRzIG9uIG9uZSBwbG90LCBnaXZlIHRoZW0gc3BhY2UgdG8gc2hpbmUKNy4gQ2xlYXIgYW5kIGNvbnNpc3RlbnQgY29sb3VyIHNjaGVtZSAtIFN0aWNrIHdpdGggdGhlIHNhbWUgY29sb3VycyBmb3IgdGhlIHNhbWUgdmFyaWFibGVzLCBhdm9pZCByZWQvZ3JlZW4gY29tYmluYXRpb25zIHdoaWNoIG1pZ2h0IGxvb2sgdGhlIHNhbWUgdG8gY29sb3VyYmxpbmQgcGVvcGxlCjguIFBsb3QgaXMgdGhlIHJpZ2h0IGRpbWVuc2lvbnMgLSBBdm9pZCBvdmVybGFwcGluZyBsYWJlbHMgYW5kIHBvaW50cy9saW5lcyB3aGljaCBtZXJnZSB0b2dldGhlciBhbmQgbWFrZSB5b3VyIGdyYXBoIGxvbmdlci93aWRlciBpZiBuZWVkZWQKOS4gTWVhc3VyZXMgb2YgdW5jZXJ0YWludHkgd2hlcmUgYXBwcm9wcmlhdGUgLSBFcnJvciBiYXJzLCBjb25maWRlbmNlIGFuZCBjcmVkaWJsZSBpbnRlcnZhbHMsIHJlbWVtYmVyIHRvIHNheSBpbiB0aGUgY2FwdGlvbiB3aGF0IHRoZXkgYXJlCjEwLiBDb25jaXNlIGFuZCBpbmZvcm1hdGl2ZSBjYXB0aW9uIC0gUmVtZW1iZXIgdG8gaW5jbHVkZSB3aGF0IHRoZSBkYXRhIHBvaW50cyBzaG93IChyYXcgZGF0YT8gTW9kZWwgcHJlZGljdGlvbnM/KSwgd2hhdCBpcyB0aGUgc2FtcGxlIHNpemUgZm9yIGVhY2ggdHJlYXRtZW50LCB0aGUgZWZmZWN0IHNpemUgYW5kIHdoYXQgbWVhc3VyZSBvZiB1bmNlcnRhaW50eSBhY2NvbXBhbmllcyBpdAoKPCEtLSAtLS0gLS0+Cgo8IS0tICMgQ2hvb3NpbmcgdGhlIHJpZ2h0IHZpc3VhbGl6YXRpb24gc29mdHdhcmUgLS0+Cgo8IS0tIFJlcHJvZHVjaWJpbGl0eSBhbmQgcmVwZWF0YWJpbGl0eSAtLT4KPCEtLSBEYXRhIGV4cGxvcmF0aW9uIHZlcnN1cyBkYXRhIHByZXNlbnRhdGlvbiAtLT4KPCEtLSBTZXBhcmF0aW9uIG9mIGNvbnRlbnQgYW5kIGRlc2lnbiAtLT4KCjwhLS0gLS0tIC0tPgoKPCEtLSAjIFRlbGxpbmcgYSBzdG9yeSBhbmQgbWFraW5nIGEgcG9pbnQgLS0+Cgo8IS0tIE1vc3QgZGF0YSB2aXN1YWxpemF0aW9uIGlzIGRvbmUgZm9yIHRoZSBwdXJwb3NlIG9mIGNvbW11bmljYXRpb24uIFdlIGhhdmUgYW4gaW5zaWdodCBhYm91dCBhIGRhdGFzZXQsIGFuZCB3ZSBoYXZlIGEgcG90ZW50aWFsIGF1ZGllbmNlLCBhbmQgd2Ugd291bGQgbGlrZSB0byBjb252ZXkgb3VyIGluc2lnaHQgdG8gb3VyIGF1ZGllbmNlLiBUbyBjb21tdW5pY2F0ZSBvdXIgaW5zaWdodCBzdWNjZXNzZnVsbHksIHdlIHdpbGwgaGF2ZSB0byBwcmVzZW50IHRoZSBhdWRpZW5jZSB3aXRoIGEgY2xlYXIgYW5kIGV4Y2l0aW5nIHN0b3J5LiBUaGUgbmVlZCBmb3IgYSBzdG9yeSBtYXkgc2VlbSBkaXN0dXJiaW5nIHRvIHNjaWVudGlzdHMgYW5kIGVuZ2luZWVycywgd2hvIG1heSBlcXVhdGUgaXQgd2l0aCBtYWtpbmcgdGhpbmdzIHVwLCBwdXR0aW5nIGEgc3BpbiBvbiB0aGluZ3MsIG9yIG92ZXJzZWxsaW5nIHJlc3VsdHMuIEhvd2V2ZXIsIHRoaXMgcGVyc3BlY3RpdmUgbWlzc2VzIHRoZSBpbXBvcnRhbnQgcm9sZSB0aGF0IHN0b3JpZXMgcGxheSBpbiByZWFzb25pbmcgYW5kIG1lbW9yeS4gLS0+Cgo8IS0tIFdoYXQgaXMgYSBzdG9yeT8gLS0+Cgo8IS0tIEJlZm9yZSB3ZSBjYW4gZGlzY3VzcyBzdHJhdGVnaWVzIGZvciB0dXJuaW5nIHZpc3VhbGl6YXRpb25zIGludG8gc3Rvcmllcywgd2UgbmVlZCB0byB1bmRlcnN0YW5kIHdoYXQgYSBzdG9yeSBhY3R1YWxseSBpcy4gQSBzdG9yeSBpcyBhIHNldCBvZiBvYnNlcnZhdGlvbnMsIGZhY3RzLCBvciBldmVudHMsIHRydWUgb3IgaW52ZW50ZWQsIHRoYXQgYXJlIHByZXNlbnRlZCBpbiBhIHNwZWNpZmljIG9yZGVyIHN1Y2ggdGhhdCB0aGV5IGNyZWF0ZSBhbiBlbW90aW9uYWwgcmVhY3Rpb24gaW4gdGhlIGF1ZGllbmNlLiBUaGUgZW1vdGlvbmFsIHJlYWN0aW9uIGlzIGNyZWF0ZWQgdGhyb3VnaCB0aGUgYnVpbGQtdXAgb2YgdGVuc2lvbiBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBzdG9yeSBmb2xsb3dlZCBieSBzb21lIHR5cGUgb2YgcmVzb2x1dGlvbiB0b3dhcmRzIHRoZSBlbmQgb2YgdGhlIHN0b3J5LiBXZSByZWZlciB0byB0aGUgZmxvdyBmcm9tIHRlbnNpb24gdG8gcmVzb2x1dGlvbiBhbHNvIGFzIHRoZSBzdG9yeSBhcmMsIGFuZCBldmVyeSBnb29kIHN0b3J5IGhhcyBhIGNsZWFyLCBpZGVudGlmaWFibGUgYXJjLiAtLT4KCjwhLS0gTmV2ZXIgYXNzdW1lIHlvdXIgYXVkaWVuY2UgY2FuIHJhcGlkbHkgcHJvY2VzcyBjb21wbGV4IHZpc3VhbCBkaXNwbGF5cy4gIC0tPgoKPCEtLSBkbyBldmVyeXRoaW5nIHdlIGNhbiB0byBoZWxwIG91ciByZWFkZXJzIHVuZGVyc3RhbmQgdGhlIG1lYW5pbmcgb2Ygb3VyIHZpc3VhbGl6YXRpb25zIGFuZCBzZWUgdGhlIHNhbWUgcGF0dGVybnMgaW4gdGhlIGRhdGEgdGhhdCB3ZSBzZWUuIFRoaXMgdXN1YWxseSBtZWFucyBsZXNzIGlzIG1vcmUuIFNpbXBsaWZ5IHlvdXIgZmlndXJlcyBhcyBtdWNoIGFzIHBvc3NpYmxlLiAtLT4KCgotLS0KCiMgU3VtbWFyeQoKVmlzdWFsaXppbmcgZGF0YSBpcyBvbmUgb2YgdGhlIG1vc3QgaW1wb3J0YW50IGFjdGl2aXRpZXMgaW4gYXBwbGllZCBzdGF0aXN0aWNzICYgaW4gc2NpZW5jZS4gCgpUaGVyZSBpcyBhIGxhcmdlIG51bWJlciBvZiBnb29kIChhbmQgYmFkKSBwcmFjdGljZXMgLT4geW91IGNhbiBxdWlja2x5IHNlZSB3aGV0aGVyIGEgY2VydGFpbiBncmFwaGljIGlzIGVmZmVjdGl2ZSBpbiBjb252ZXlpbmcgaXRzIG1lc3NhZ2UKCkltcG9ydGFudCBvcHRpb25zOiAKCi0gcGxvdCB0eXBlICh3aGF0IGlzIGNhbGxlZCBhIGdlb20gaW4gZ2dwbG90MikKLSBwcm9wb3J0aW9ucyAoaW5jbC4gYXNwZWN0IHJhdGlvcykgCi0gY29sb3JzLiAKClRoZSBncmFtbWFyIG9mIGdyYXBoaWNzIGlzIGEgcG93ZXJmdWwgc2V0IG9mIGNvbmNlcHRzIHRvIHJlYXNvbiBhYm91dCBncmFwaGljcyBhbmQgdG8gY29tbXVuaWNhdGUgb3VyIGludGVudGlvbnMgZm9yIGEgZGF0YSB2aXN1YWxpemF0aW9uIHRvIGEgY29tcHV0ZXIuCgpDcmVhdGluZyB5b3VyIG93biB2aXN1YWxpemF0aW9ucyBpcyBpbiBtYW55IHdheXMgbGlrZSBnb29kIHdyaXRpbmcuIEl0IGlzIGV4dHJlbWVseSBpbXBvcnRhbnQsIGJ1dCB0aGVyZSBpcyBubyBzaW1wbGUgcmVjaXBlIGZvciBpdC4gCgpMb29rIGNhcmVmdWxseSBhdCBsb3RzIG9mIHZpc3VhbGl6YXRpb25zIG1hZGUgYnkgb3RoZXJzICYgZXhwZXJpbWVudCB3aXRoIG1ha2luZyB5b3VyIG93biB2aXN1YWxpemF0aW9ucyB0byBsZWFybiB0aGUgcm9wZXMKCipZZXMsIHdlIGp1c3Qgc2NyYXRjaGVkIHRoZSBzdXJmYWNlISogRGF0YSB2aXogaXMgYSBzY2llbnRpZmljIGRpc2NpcGxpbmUgaW4gaXRzIG93bgoKLS0tCgojIFNlc3Npb24gaW5mb3JtYXRpb24gey19CgpXZSByZXBvcnQgdGhlIHZlcnNpb24gbnVtYmVycyBvZiBSIGFuZCBhbGwgdGhlIHBhY2thZ2VzIHVzZWQgaW4gdGhpcyBzZXNzaW9uLiAKCldoeT8KCkl0IGlzIGdvb2QgcHJhY3RpY2UgdG8gYWx3YXlzIGtlZXAgc3VjaCBhIHJlY29yZCBvZiB0aGlzIQpCeSBpbmNsdWRpbmcgdGhpcyBhdCB0aGUgYm90dG9tIG9mIGEgc2NyaXB0LCB5b3VyIHJlcG9ydHMgd2lsbCBiZWNvbWUgbW9yZSByZXByb2R1Y2libGUuCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==